<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html lang="zh-CN"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta http-equiv="Content-Language" content="zh-CN"><link href="stylesheet.css" media="all" rel="stylesheet" type="text/css">
<title>游标</title>
<script> var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?d286c55b63a3c54a1e43d10d4c203e75"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); </script>
</head><body class="SECT1">
<div>
<table summary="Header navigation table" width="100%" border="0" cellpadding="0" cellspacing="0">
<tr><th colspan="5" align="center" valign="bottom">PostgreSQL 8.2.3 中文文档</th></tr>
<tr><td width="10%" align="left" valign="top"><a href="plpgsql-control-structures.html" accesskey="P">后退</a></td><td width="10%" align="left" valign="top"><a href="plpgsql.html">快退</a></td><td width="60%" align="center" valign="bottom">章37. PL/pgSQL - SQL 过程语言</td><td width="10%" align="right" valign="top"><a href="plpgsql.html">快进</a></td><td width="10%" align="right" valign="top"><a href="plpgsql-errors-and-messages.html" accesskey="N">前进</a></td></tr>
</table>
<hr align="LEFT" width="100%"></div>
<div class="SECT1"><h1 class="SECT1"><a name="PLPGSQL-CURSORS">37.8. 游标</a></h1><a name="AEN40340"></a>
<p>如果不想一次执行整个命令，可以设置一个封装该命令的<i class="FIRSTTERM">游标</i>，然后每次读取几行命令结果。这么干的一个原因是在结果包含数量非常大的行时避免内存耗尽。不过 PL/pgSQL 用户不必担心这个，因为 <tt class="LITERAL">FOR</tt> 循环自动在内部使用一个游标以避免内存问题。一个更有趣的用法是某个函数可以返回一个它创建的游标的引用，这样就允许调用者读取各行。从而提供了一种从函数返回一个结果集的手段。</p>
<div class="SECT2"><h2 class="SECT2"><a name="PLPGSQL-CURSOR-DECLARATIONS">37.8.1. 声明游标变量</a></h2>
<p>所有在 PL/pgSQL 里对游标的访问都是通过游标变量实现的，它总是特殊的数据类型 <tt class="TYPE">refcursor</tt> 。创建游标变量的一个方法是把它声明为一个类型为 <tt class="TYPE">refcursor</tt> 的变量。另外一个方法是使用游标声明语法，像下面这样：</p>
<pre class="SYNOPSIS"><tt class="REPLACEABLE"><i>name</i></tt> CURSOR [<span class="OPTIONAL">( <tt class="REPLACEABLE"><i>arguments</i></tt> )</span>] FOR <tt class="REPLACEABLE"><i>query</i></tt>;</pre>
<p>出于兼容 Oracle 的考虑，<tt class="LITERAL">FOR</tt> 可以被替换为 <tt class="LITERAL">IS</tt> 。如果有 <tt class="REPLACEABLE"><i>arguments</i></tt> ，那么它是一个逗号分隔的 <tt class="LITERAL"><tt class="REPLACEABLE"><i>name</i></tt> <tt class="REPLACEABLE"><i>datatype</i></tt></tt> 对的列表，它们定义那些将会用参数值替换掉的所给出命令中的名字。实际用于代换这些名字的数值将在在游标打开之后声明。</p>
<p>几个例子：</p>
<pre class="PROGRAMLISTING">DECLARE
    curs1 refcursor;
    curs2 CURSOR FOR SELECT * FROM tenk1;
    curs3 CURSOR (key integer) IS SELECT * FROM tenk1 WHERE unique1 = key;</pre>
<p>所有这三个变量都是 <tt class="TYPE">refcursor</tt> 类型，但是第一个可以用于任何命令，而第二个已经<i class="FIRSTTERM">绑定</i>了一个声明完整的命令，最后一个是绑定了一个带参数的命令。<tt class="LITERAL">key</tt> 将在游标打开的时候被代换成一个整数。变量 <tt class="LITERAL">curs1</tt> 可以称之为<i class="FIRSTTERM">未绑定的</i>，因为它没有和任何查询相绑定。</p>
</div>
<div class="SECT2"><h2 class="SECT2"><a name="PLPGSQL-CURSOR-OPENING">37.8.2. 打开游标</a></h2>
<p>在你使用游标检索行之前，你必需先<i class="FIRSTTERM">打开</i>它。这是和 SQL 命令 <tt class="COMMAND">DECLARE CURSOR</tt>相等的操作。PL/pgSQL 有三种形式的 <tt class="COMMAND">OPEN</tt> 语句，两种用于未绑定的游标变量，另外一种用于已绑定的游标变量。</p>
<div class="SECT3"><h3 class="SECT3"><a name="AEN40379">37.8.2.1. <tt class="COMMAND">OPEN FOR</tt> <tt class="REPLACEABLE"><i>query</i></tt></a></h3>
<pre class="SYNOPSIS">OPEN <tt class="REPLACEABLE"><i>unbound_cursor</i></tt> FOR <tt class="REPLACEABLE"><i>query</i></tt>;</pre>
<p>该游标变量打开并且执行给出的查询。游标不能是已经打开的，并且它必需是声明为一个未绑定的游标(也就是声明为一个简单的 <tt class="TYPE">refcursor</tt> 变量)。查询必须是一条 <tt class="COMMAND">SELECT</tt> 或者其它返回行的东西(比如 <tt class="COMMAND">EXPLAIN</tt>)。查询是和其它在 PL/pgSQL 里的 SQL 命令平等对待的：先代换 PL/pgSQL 的变量名，而且执行计划为将来可能的复用缓存起来。</p>
<p>一个例子：</p>
<pre class="PROGRAMLISTING">OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey;</pre>
</div>
<div class="SECT3"><h3 class="SECT3"><a name="AEN40394">37.8.2.2. <tt class="COMMAND">OPEN FOR EXECUTE</tt></a></h3>
<pre class="SYNOPSIS">OPEN <tt class="REPLACEABLE"><i>unbound_cursor</i></tt> FOR EXECUTE <tt class="REPLACEABLE"><i>query_string</i></tt>;</pre>
<p>打开游标变量并且执行给出的查询。游标不能是已打开的，并且必须声明为一个未绑定的游标(也就是一个简单的 <tt class="TYPE">refcursor</tt> 变量)。命令是用和那些用于 <tt class="COMMAND">EXECUTE</tt> 命令一样的方法声明的字符串表达式，这样，就有了命令可以在两次运行间发生变化的灵活性。</p>
<p>一个例子：</p>
<pre class="PROGRAMLISTING">OPEN curs1 FOR EXECUTE 'SELECT * FROM ' || quote_ident($1);</pre>
</div>
<div class="SECT3"><h3 class="SECT3"><a name="AEN40405">37.8.2.3. 打开一个绑定的游标</a></h3>
<pre class="SYNOPSIS">OPEN <tt class="REPLACEABLE"><i>bound_cursor</i></tt> [<span class="OPTIONAL">( <tt class="REPLACEABLE"><i>argument_values</i></tt> )</span>];</pre>
<p>这种形式的 <tt class="COMMAND">OPEN</tt> 用于打开一个游标变量，该游标变量的命令是在声明的时候和它绑定在一起的。游标不能是已经打开的。当且仅当该游标声明为接受参数的时候，语句中才必需出现一个实际参数值表达式的列表。这些值将代换到命令中。一个绑定的游标的命令计划总是认为可缓冲的，这种情况下没有等效的 <tt class="COMMAND">EXECUTE</tt> 。</p>
<p>例子：</p>
<pre class="PROGRAMLISTING">OPEN curs2;
OPEN curs3(42);</pre>
</div>
</div>
<div class="SECT2"><h2 class="SECT2"><a name="PLPGSQL-CURSOR-USING">37.8.3. 使用游标</a></h2>
<p>一旦你已经打开了一个游标，那么你就可以用这里描述的语句操作它。</p>
<p>这些操作不需要发生在和打开该游标开始操作的同一个函数里。你可以从函数里返回一个 <tt class="TYPE">refcursor</tt> 值，然后让调用者操作该游标。在内部，<tt class="TYPE">refcursor</tt> 值只是一个包含该游标命令的活跃查询的信使的字符串名。这个名字可以传来传去，可以赋予其它 <tt class="TYPE">refcursor</tt> 变量等等，也不用担心扰乱信使。</p>
<p>所有信使在事务的结尾都会隐含地关闭。因此一个 <tt class="TYPE">refcursor</tt> 值只能在该事务结束前用于引用一个打开的游标。</p>
<div class="SECT3"><h3 class="SECT3"><a name="AEN40425">37.8.3.1. <tt class="LITERAL">FETCH</tt></a></h3>
<pre class="SYNOPSIS">FETCH <tt class="REPLACEABLE"><i>cursor</i></tt> INTO <tt class="REPLACEABLE"><i>target</i></tt>;</pre>
<p><tt class="COMMAND">FETCH</tt> 从游标中检索下一行到目标中，目标可以是一个行变量、记录变量、逗号分隔的普通变量列表，就像 <tt class="COMMAND">SELECT INTO</tt> 里一样。和 <tt class="COMMAND">SELECT INTO</tt> 一样，可以使用特殊变量 <tt class="LITERAL">FOUND</tt> 检查是否检索出一个行。</p>
<p>一个例子：</p>
<pre class="PROGRAMLISTING">FETCH curs1 INTO rowvar;
FETCH curs2 INTO foo, bar, baz;</pre>
</div>
<div class="SECT3"><h3 class="SECT3"><a name="AEN40438">37.8.3.2. <tt class="LITERAL">CLOSE</tt></a></h3>
<pre class="SYNOPSIS">CLOSE <tt class="REPLACEABLE"><i>cursor</i></tt>;</pre>
<p><tt class="COMMAND">CLOSE</tt> 关闭支撑在一个打开的游标下面的信使。这样就可以在事务结束之前释放资源，或者释放掉该游标变量，用于稍后再次打开。</p>
<p>一个例子：</p>
<pre class="PROGRAMLISTING">CLOSE curs1;</pre>
</div>
<div class="SECT3"><h3 class="SECT3"><a name="AEN40447">37.8.3.3. 返回游标</a></h3>
<p>PL/pgSQL 函数可以向调用者返回游标。这个功能用于从函数里返回多行或多列，特别是巨大的结果集。要想这么做，该函数必须打开游标并且把该游标的名字返回给调用者，或者简单的使用指定的入口名或调用者已知的名字打开游标。调用者然后从游标里抓取行。游标可以由调用者关闭，或者是在事务结束的时候自动关闭。</p>
<p>函数返回的游标名可以由调用者声明或者自动生成。要声明一个信使的名字，只要在打开游标之前，给 <tt class="TYPE">refcursor</tt> 变量赋予一个字符串就可以了。<tt class="TYPE">refcursor</tt> 变量的字符串值将被 <tt class="COMMAND">OPEN</tt> 当作下层的信使的名字使用。不过，如果 <tt class="TYPE">refcursor</tt> 变量是空，那么 <tt class="COMMAND">OPEN</tt> 将自动生成一个和现有信使不冲突的名字，然后将它赋予 <tt class="TYPE">refcursor</tt> 变量。</p>
<div class="NOTE">
<blockquote class="NOTE">
<p><b>【注意】</b> 一个绑定的游标变量其名字初始化为对应的字符串值，因此信使的名字和游标变量名同名，除非程序员在打开游标之前通过赋值覆盖了这个名字。但是一个未绑定的游标变量初始化的时候缺省是空，因此它会收到一个自动生成的唯一名字，除非被覆盖。</p>
</blockquote>
</div>
<p>下面的例子显示了一个调用者声明游标名字的方法：</p>
<pre class="PROGRAMLISTING">CREATE TABLE test (col text);
INSERT INTO test VALUES ('123');

CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS '
BEGIN
    OPEN $1 FOR SELECT col FROM test;
    RETURN $1;
END;
' LANGUAGE plpgsql;

BEGIN;
SELECT reffunc('funccursor');
FETCH ALL IN funccursor;
COMMIT;</pre>
<p>下面的例子使用了自动生成的游标名：</p>
<pre class="PROGRAMLISTING">CREATE FUNCTION reffunc2() RETURNS refcursor AS '
DECLARE
    ref refcursor;
BEGIN
    OPEN ref FOR SELECT col FROM test;
    RETURN ref;
END;
' LANGUAGE plpgsql;

BEGIN;
SELECT reffunc2();

      reffunc2
--------------------
 &lt;unnamed cursor 1&gt;
(1 row)

FETCH ALL IN "&lt;unnamed cursor 1&gt;";
COMMIT;</pre>
<p>下面的例子显示了从一个函数里返回多个游标的方法：</p>
<pre class="PROGRAMLISTING">CREATE FUNCTION myfunc(refcursor, refcursor) RETURNS SETOF refcursor AS $$
BEGIN
    OPEN $1 FOR SELECT * FROM table_1;
    RETURN NEXT $1;
    OPEN $2 FOR SELECT * FROM table_2;
    RETURN NEXT $2;
END;
$$ LANGUAGE plpgsql;

-- 需要在事务里使用游标。
BEGIN;

SELECT * FROM myfunc('a', 'b');

FETCH ALL FROM a;
FETCH ALL FROM b;
COMMIT;</pre>
</div>
</div>
</div>
<div>
<hr align="LEFT" width="100%">
<table summary="Footer navigation table" width="100%" border="0" cellpadding="0" cellspacing="0">
<tr><td width="33%" align="left" valign="top"><a href="plpgsql-control-structures.html" accesskey="P">后退</a></td><td width="34%" align="center" valign="top"><a href="index.html" accesskey="H">首页</a></td><td width="33%" align="right" valign="top"><a href="plpgsql-errors-and-messages.html" accesskey="N">前进</a></td></tr>
<tr><td width="33%" align="left" valign="top">控制结构</td><td width="34%" align="center" valign="top"><a href="plpgsql.html" accesskey="U">上一级</a></td><td width="33%" align="right" valign="top">错误和消息</td></tr>
</table>
</div>
</body></html>